package com.pomelo.common;

import com.jcraft.jsch.*;
import com.pomelo.config.FileConfig;
import com.pomelo.thread.FileThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Vector;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class FtpUtils {

    /*
     * 建立连接
     */
    private static Session getSession() {
        Logger logger = LoggerFactory.getLogger(FtpUtils.class);
        JSch jsch = new JSch();
        Session session;
        try {
            if (Integer.parseInt(FileConfig.getPort()) <= 0) {
                //连接服务器，采用默认端口
                session = jsch.getSession(FileConfig.getUser(), FileConfig.getIp());
            } else {
                //采用指定的端口连接服务器
                session = jsch.getSession(FileConfig.getUser(), FileConfig.getIp(), Integer.parseInt(FileConfig.getPort()));
            }
        } catch (JSchException e) {
            throw new RuntimeException(e);
        }
        //如果服务器连接不上，则抛出异常
        if (session == null) {
            logger.error("session is null");
            return null;
        } else {
            //设置登陆主机的密码
            session.setPassword(FileConfig.getPassword());//设置密码
            //设置第一次登陆的时候提示，可选值：(ask | yes | no)
            session.setConfig("StrictHostKeyChecking", "no");
            //设置登陆超时时间
            try {
                session.connect(30000);
                logger.info("登录成功！");
                return session;
            } catch (JSchException e) {
                logger.error(e.getMessage());
                return null;
            }
        }
    }

    /**
     * 获取文件夹所有文件
     *
     * @return 所有文件信息
     */
    public static Vector<ChannelSftp.LsEntry> getAllFile() throws SftpException, JSchException {
        Session session = getSession();
        //创建sftp通信通道
        Channel channel = (Channel) session.openChannel("sftp");
        channel.connect(1000);
        ChannelSftp sftp = (ChannelSftp) channel;
        Vector<ChannelSftp.LsEntry> entries = sftp.ls(FileConfig.getPath());
        for (ChannelSftp.LsEntry entry : entries) {
            readFileThread(sftp, entry);
        }
        //Vector<ChannelSftp.LsEntry> entries = readFile(sftp, FileConfig.getPath());
        sftp.disconnect();
        session.disconnect();
        return entries;
    }

    /**
     * 使用多线程获取文件夹信息
     *
     * @param sftp  sftp连接
     * @param entry 文件夹实体对象
     */
    public static void readFileThread(ChannelSftp sftp, ChannelSftp.LsEntry entry) throws SftpException {
        // 通过深度遍历获取到所有文件-->输出，获取到所有文件夹存储到next中
        Vector<ChannelSftp.LsEntry> vector1 = new Vector<>();
        Vector<ChannelSftp.LsEntry> vector2 = new Vector<>();
        Vector<ChannelSftp.LsEntry> temp = null, next = null;
        vector1.add(entry);
        int loop = 1;
        while (loop++ < 3) {
            temp = temp == vector1 ? vector2 : vector1;
            next = next == vector2 ? vector1 : vector2;
            for (ChannelSftp.LsEntry en : temp) {
                if (en != null) {
                    if (en.getAttrs().isDir() && !"..".equals(en.getFilename()) && !".".equals(en.getFilename())) {
                        String path;
                        if (en.getFilename().equals(entry.getFilename())) {
                            path = FileConfig.getPath() + "/" + en.getFilename();
                        } else
                            path = FileConfig.getPath() + "/" + entry.getFilename() + "/" + en.getFilename();
                        System.out.println(path);
                        next.addAll(sftp.ls(path));
                    } else {
                        System.out.println(en.getFilename());
                    }
                }
            }
            temp.clear();
        }

        ThreadPoolExecutor op = new ThreadPoolExecutor(10, 20, 1000,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10),
                (r, executor) -> System.out.println(r.toString() + "执行了拒绝策略"));
        for (ChannelSftp.LsEntry en : next) {
            op.execute(new FileThread(sftp, en));
        }
        op.shutdown();
        while (!op.isTerminated())
            ;
    }

    private static Vector<ChannelSftp.LsEntry> readFile(ChannelSftp sftp, String path) throws SftpException {
        System.out.println(path);
        Vector<ChannelSftp.LsEntry> vector = sftp.ls(path);
        Vector<ChannelSftp.LsEntry> files = new Vector<>();
        assert vector != null;
        for (ChannelSftp.LsEntry entry : vector) {
            // 去除 .. 及 . 目录，如果是目录递归目录下的文件
            if (!"..".equals(entry.getFilename()) && !".".equals(entry.getFilename())) {
                if (entry.getAttrs().isDir()) {
                    System.out.println();
                    files.addAll(readFile(sftp, path + "/" + entry.getFilename()));
                }
                System.out.print(entry.getFilename() + "\t");
                files.add(entry);
            }
        }
        return files;
    }


    /**
     * 上传单个文件
     *
     * @param file 要上传的文件
     */
    private static void upload(ChannelSftp sftp, File file) throws FileNotFoundException, SftpException {
        sftp.put(file.getAbsolutePath(), file.getName());
        System.out.println(file.getAbsolutePath() + "上传完毕！");
    }

    /**
     * 上传文件夹下所有文件
     *
     * @param dir 目录
     */
    public static void batch(String dir) throws FileNotFoundException, JSchException, SftpException {
        Session session = getSession();
        //创建sftp通信通道
        Channel channel = (Channel) session.openChannel("sftp");
        channel.connect(1000);
        ChannelSftp sftp = (ChannelSftp) channel;
        // 将文件上传到哪一个目录
        sftp.cd(FileConfig.getPath());
        File file = new File(dir);
        if (!file.isDirectory()) { // 如果不是目录 执行单个的上传操作
            upload(sftp, new File(dir));
        } else {
            File[] files = file.listFiles((f) -> !f.isDirectory());
            assert files != null;
            for (File f : files) {
                upload(sftp, f);
            }
            System.out.println(file.getAbsolutePath() + "上传完毕");
        }
        sftp.disconnect();
        session.disconnect();
    }

    /**
     * 下载文件
     *
     * @param dir 文件名
     */
    public static void download(String dir) throws JSchException, SftpException, IOException {
        Session session = getSession();
        //创建sftp通信通道
        Channel channel = (Channel) session.openChannel("sftp");
        channel.connect(1000);
        ChannelSftp sftp = (ChannelSftp) channel;
        sftp.cd(FileConfig.getPath());
        InputStream inputStream = sftp.get(dir);
        File file = new File(FileConfig.getDownloadPath() + File.separator + dir);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        inputStream.transferTo(new FileOutputStream(file));
        System.out.println(dir + "下载完毕，下载目录" + FileConfig.getDownloadPath());
        sftp.disconnect();
        session.disconnect();
    }

}
